#ifndef PATH_MAP_HPP
#define PATH_MAP_HPP

#include <cstring>
#include "wolf.hpp"
#include <stack>

#define BLOCK       0x1 //Useful on edit mode, Mark if it is not passable
#define VISITED    0x2 //ALl others on run mode, this marks if it was returned.
#define MARKED      0x4 //An yellow mark
#define CIRCLED     0x8 //A MAGENTA CIRCLE
#define LK_EAST     0x10
#define LK_SOUTH    0x20
#define LK_WEST     0x40
#define LK_NORTH    0x80

#define EAST    0
#define SOUTH   1
#define WEST    2
#define NORTH   3

inline Uint8 turnClock(Uint8 dir)
{
    return (dir +1)%4;
}

inline Uint8 turnAntiClock(Uint8 dir)
{
    return (dir+3)%4;
}

inline Uint8 reverseDir(Uint8 dir)
{
    return (dir+2)%4;
}

inline bool isClock(Uint8 dir1, Uint8 dir2)
{
    return turnClock(dir1) == dir2;
}

inline Uint8 turn(Uint8 dir, bool clockwise)
{
    if(clockwise) return turnClock(dir);
    else return turnAntiClock(dir);
}

using namespace std;
class Map
{
	public:
        Uint8 forbiden; ///< An invalid position
        /**
         * @brief Creates a new map
         **/
		Map(unsigned nLines, unsigned nCols): forbiden(BLOCK)
		{
		    _nLines = nLines;
		    _nCols = nCols;
		    _pos = new Uint8[nLines*nCols];
		    clear();
            _sX = _sY = 0;
            _dX = nCols-1;
            _dY = nLines-1;
            _x = _sX;
            _y = _sY;
            _dir = 0xFF;
		}
		/**
		 * @brief Deletes a map
		 **/
		~Map()
		{
		    delete []_pos;
		}

		/**
		 * @brief Access a determined tile
		 **/
		Uint8 &operator()(unsigned line, unsigned col)
		{
		    if(line < _nLines && col < _nCols)
                return *(_pos + (line*_nCols+col));
            else
                return forbiden;
		}

		void clear(void)
		{
		    memset(_pos, 0, _nLines*_nCols);
		}

        void reset(void)
        {
            for(unsigned i=0; i< _nLines; ++i)
            {
                for(unsigned j=0; j < _nCols; ++j)
                {
                    (*this)(i,j) &= BLOCK;
                }
            }
            _x = _sX;
            _y = _sY;
            _dir = 0xFF;
        }

		unsigned nLines(void){return _nLines;}
		unsigned nCols(void){return _nCols;}
		unsigned sX(void){return _sX;}
		void sX(unsigned val){_sX = val;}
		unsigned sY(void){return _sY;}
		void sY(unsigned val){_sY = val;}
		unsigned dX(void){return _dX;}
		void dX(unsigned val){_dX = val;}
		unsigned dY(void){return _dY;}
		void dY(unsigned val){_dY = val;}
		unsigned x(void){return _x;}
		void x(unsigned val){_x = val;}
		unsigned y(void){return _y;}
		void y(unsigned val){_y = val;}

        bool isForbiden(Uint8 dir)
        {
            unsigned nX = dir==WEST?_x-1:(dir==EAST?_x+1:_x);
            unsigned nY = dir==NORTH?_y-1:(dir==SOUTH?_y+1:_y);
            Uint8 &tile = (*this)(nY, nX);
            if(&tile == &forbiden) return true;
            return false;
        }

        bool hasFlag(Uint8 flag, Uint8 dir)
        {
            unsigned nX = dir==WEST?_x-1:(dir==EAST?_x+1:_x);
            unsigned nY = dir==NORTH?_y-1:(dir==SOUTH?_y+1:_y);
            Uint8 &tile = (*this)(nY, nX);
            if(&tile == &forbiden) return true;
            else return tile & flag;
        }

        bool isBlock(Uint8 dir)
        {
            return hasFlag(BLOCK, dir);
        }

        bool wasHere(Uint8 dir)
        {
            return hasFlag((dir+2)%4, dir);
        }

        Uint8 dir( void)
        {
            return _dir;
        }

        void dir(Uint8 val)
        {
            _dir = val;
        }

        void walk(Uint8 dir)
        {
            Uint8 &tile = (*this)(_y, _x);
            unsigned nX = dir==WEST?_x-1:(dir==EAST?_x+1:_x);
            unsigned nY = dir==NORTH?_y-1:(dir==SOUTH?_y+1:_y);
            Uint8 &oTile = (*this)(nY, nX);
            if(&tile == &forbiden || &oTile == &forbiden) return;
            _dir = dir;
            if(tile & (0x10 <<dir)) // Returning
            {
                tile |= VISITED;
            }
            else
            {
                tile |= 0x10 <<dir;
                oTile |= 0x10 << ((dir+2)%4);
            }
            _x = nX;
            _y = nY;
        }

        void mark( void )
        {
            Uint8 &tile = (*this)(_y, _x);
            tile |= MARKED;
        }

        void circle( void )
        {
            Uint8 &tile = (*this)(_y, _x);
            tile |= CIRCLED;
        }

	private:
        Uint8 *_pos;
        unsigned _nLines, _nCols;
        unsigned _sX, _sY;
        unsigned _dX, _dY;
        unsigned _x, _y;
        Uint8 _dir;
        stack<Uint8> _steps;
};


#endif // PATH_MAP_HPP

